package com.example.sensitive;

import com.example.enums.SensitiveEnum;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;

import java.io.IOException;
import java.util.Objects;

/**
 * 自定义序列化器
 */
public class MySensitiveSerialize extends JsonSerializer<String> implements ContextualSerializer {

    // 脱敏类型
    private SensitiveEnum type;

    // 不加无参构造会报错
    public MySensitiveSerialize() {
    }

    private MySensitiveSerialize(final SensitiveEnum type) {
        this.type = type;
    }

    /**
     * 实际序列化逻辑，把Java对象字段写入JSON
     */
    @Override
    public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        switch (this.type) {
            case CHINESE_NAME: {
                // 写入json
                jsonGenerator.writeString(SensitiveInfoUtil.chineseName(s));
                break;
            }
            case ID_CARD: {
                jsonGenerator.writeString(SensitiveInfoUtil.idCardNum(s));
                break;
            }
            case FIXED_PHONE: {
                jsonGenerator.writeString(SensitiveInfoUtil.fixedPhone(s));
                break;
            }
            case MOBILE_PHONE: {
                jsonGenerator.writeString(SensitiveInfoUtil.mobilePhone(s));
                break;
            }
            case ADDRESS: {
                jsonGenerator.writeString(SensitiveInfoUtil.address(s, 4));
                break;
            }
            case EMAIL: {
                jsonGenerator.writeString(SensitiveInfoUtil.email(s));
                break;
            }
            case BANK_CARD: {
                jsonGenerator.writeString(SensitiveInfoUtil.bankCard(s));
                break;
            }
            case CNAPS_CODE: {
                jsonGenerator.writeString(SensitiveInfoUtil.cnapsCode(s));
                break;
            }
        }
    }

    /**
     * 上下文初始化方法：Jackson在序列化每个字段前会调用这个方法，可以根据字段上的注解生成一个带配置的序列化器
     * 目的：在序列化某个字段之前，根据字段的类型与注解决定返回哪一个 JsonSerializer —— 若字段是 String 且标注了 @SensitiveWrapped，就返回一个“带脱敏配置”的序列化器实例；
     * 否则返回 Jackson 默认的序列化器。方法的核心是把注解信息（annotation.value()）传入序列化器，从而实现“同一个序列化器类，不同字段不同脱敏规则”的能力（ContextualSerializer 的典型用法）
     */
    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
        // 为空直接跳过
        if (beanProperty != null) {
            // 非String类直接跳过
            JavaType type = beanProperty.getType();
            if (Objects.equals(type.getRawClass(), String.class)) {
                // 拿到注解
                SensitiveWrapped annotation = beanProperty.getAnnotation(SensitiveWrapped.class);
                if (annotation == null) {
                    annotation = beanProperty.getContextAnnotation(SensitiveWrapped.class);
                }
                if (annotation != null) {
                    // 如果能得到注解，就将注解的value传入序列化器中
                    return new MySensitiveSerialize(annotation.value());
                }
            }
            return serializerProvider.findValueSerializer(type, beanProperty);
        }
       // return serializerProvider.findNullValueSerializer(beanProperty);
        // 优化：beanProperty = null时返回默认的即可：
        return serializerProvider.findValueSerializer(this.handledType(), null);
    }

}
